>>>>>>>>>>>>>>>>>>>>> CP/M-Net News <<<<<<<<<<<<<<<<<<<<<<<<

============================================================
Number 1              January, 1980        Volume 1, Issue 1
============================================================

 Printed  monthly  (at worst quarterly) to inform user's  of 
RCPM Systems to the latest software news,  information,  and 
updates   of   public   domain   software   accessible   via 
telephone/modem transfer.  Yearly subscription for copies of 
the  CP/M-Net News may be obtained by mailing $18.00  (check 
or money orders only) to Kelly Smith,  CP/M-Net,  3055  Waco 
Avenue,  Simi Valley,  California 93063.  CP/M-Net is a non-
profit  orginization and all money received on subscriptions 
are utilized for the sustaining and enhancments of the CP/M-
Net System.

 If  you  would  like to contribute an  article,  include  a 
column containing your area of interest and   expertise,  or 
participate  in an open forum for conversation and  transfer 
of  ideas,  feel free to send it to the CP/M-Net System  and 
indicate  that you would like it to be included in the CP/M-
Net News...if possible,   use WordStar (trademark,  MicroPro 
International)  or  Electric  Pencil   (trademark,   Micheal 
Shrayer) in 60 column format.
 
Note: CP/M is a registered trademark of Digital Research


               MODEM/XMODEM Protocol Explained
               by Kelly Smith, CP/M-Net "SYSOP"
                      January 8,1980

 I  thought that it may be of some interest to those of  you 
that  use  the MODEM/XMODEM file transfer capability of  the 
CP/M-Net,  to get a little insight as to the  communications 
protocol  (i.e.  "handshaking  method") used by the  system. 

 Herein  lies the details of a very good (not perfect)  data 
communications  protocol  that  has become  the  "de  facto" 
standard  for various remote CP/M systems (RCPM's) that  are 
accessible across the country (refer to RCPMLST5.DOC on  all 
RCPM's  for access numbers and note that the "digit  number" 
in that list changes as new system are listed).  I also wish 
to give credit to Ward Christensen (the "original" CBBS) for 
writing  MODEM.ASM (CPMUG Volume 25.11) and Keith  Petersen, 
Bruce Ratoff, Dave Hardy, Rod Hart, Tom "C" (we know who you 
are Tom!),  and others,  for enhancements to Ward's original 
program that we now call XMODEM (external modem).

 Data is sent in 128 byte blocks with sequentially  numbered 
blocks, and appended by a single checksum at the end of each 
block. As the receiving computer acquires the incoming data, 
it  performs it's own checksum and upon each completion of a 
block,  it  compares it's checksum result with that  of  the 
sending  computers.  If  the receiving computer matches  the 
checksum of the sending computer, it transmits an ACK (ASCII 
code protocol character for ACKNOWLEDGE (04 Hex, Control-F)) 
back to the sending computer.  The ACK therefore means "alls 
well  on  this  end,  send  some  more...".  Notice  in  the 
following example,  that the sending computer will  transmit 
an  "initial  NAK"  (ASCII protocol character  for  NEGATIVE 
ACKNOWLEDGE (15 Hex,  Control-U))...or,  "that wasn't  quite 
right, please send again". Due to the asynchronous nature of 
the  initial  "hook-up"  between  the  two  computers,   the 
receiving  computer  will "time-out" looking for  data,  and 
send the NAK as the "cue" for the sending computer to  begin 
transmission. The  sending computer knows that the receiving 
computer  will  "time-out",  and uses this fact to  "get  in 
sync"...The  sending computer responds to the "initial  NAK" 
with  a  SOH  (ASCII code protocol character  for  START  OF 
HEADING (01 Hex,  Control-A)), sends the first block number, 
sends the 2' complement of the block number (VERY important, 
I will discuss this later...), sends 128 bytes of 8 bit data 
(thats  why  we can transfer ".COM" files),  and  finally  a 
checksum,  where  the checksum is calculated by summing  the 
SOH,  the block number, the block number 2's complement, and 
the 128 bytes of data.

Receiving Computer:

----/NAK/------------------------/ACK/----------------------
     15H                          06H 

Sending Computer:

--------/SOH/BLK#/BLK#/DATA/CSUM/---/SOH/BLK#/BLK#/DATA/etc.
         01H 001H 0FEH 8bit 8bit     01H 002H 0FDH 8bit ....  

 This  process continues,  with the next 128 bytes,  IF  the 
block  was ACK'ed by the receiving computer,  and  then  the 
next sequential block number and it's 2's complement, etc.

 But  what  happens  if the  block  is  NAK'ed?...easy,  the 
sending computer just re-sends the previous block.  Now  the 
hard  part...what if the sending computer transmits a block, 
the  receiving computer gets it and sends an  ACK,  but  the 
sender  does not see it?...The sending computer thinks  that 
it   has  failed  and  after  10  seconds  re-transmits  the 
block...ARGH!...the  receiving  computer has  "stashed"  the 
data  in  memory or on disk (data is written to  disk  after 
receiving 16 blocks),  the receiving computer is now 1 block 
AHEAD of the transmiting computer!  Here comes the operation 
of the block numbers...The receiver detects that this is the 
last  block  (all over again),  and transmits back  an  ACK, 
throws   away   the  block,   and   (effectively)   "catches 
up"...clever!  Whats more, the integrity of the block number 
is verified by the receiving computer, because it "sums" the 
SOH  (01 Hex) with the block number plus the 2's  complement 
of  the  block number),  and the result MUST BE zero  for  a 
proper  transfer  (e.g.  01+01+FE hex =  00,  on  the  first 
block). The sequence of events then, looks like this: 

Receiving Computer:

----/ACK/-----------------------/NAK/-----------------------
     06H                         15H  

Sending Computer:

CSUM/---/SOH/BLK#/BLK#/DATA/CSUM/---/SOH/BLK#/BLK#/DATA/etc.
8bit     01H 003H 0FCH 8bit 8bit     01H 003H 0FCH 8bit ....

 Normal completion of data transfers will then conclude with 
an  EOT (ASCII code protocol END OF  TRANSMISSION,  04  Hex, 
Control-D)  from the sending computer,  and a final ACK from 
the  receiving computer.  Unfortunately,  if  the  receiving 
computer  misses the EOT,  it will continue to wait for  the 
next block (sending NAK's every 10 seconds,  up to 10 times) 
and eventually "time-out".  This is rarely the case however, 
and  although  not  "bullet-proof",  it is a  very  workable 
protocol.

Receiving Computer:

----/ACK/---/ACK/"Transfer Complete"/A>(or B>)
     06H     06H ................................

Sending Computer:

CSUM/---/EOT/---/A>(or B>)
8bit     04H .............

 In   some  case,   where  the  telephone  transmission   is 
repeatedly  "trashed" (weak signals,  multiple noise "hits", 
etc.),   the  receiving  computer  (and  operator)  will  be 
provided the option to quit.  Here,  the operator enters "R" 
or  "Q" in response to "Retry or Quit?" (after 10  retries), 
and  if quit is envoked by the operator,  a CAN (ASCII  code 
protocol CANCEL, 18 Hex, Control-X) is sent by the receiving 
computer to cancel the entire transfer session (Note:  is is 
possible   to  "garble"  an  ACK  to  a   CAN,   and   abort 
prematurley):

Receiving Computer:

----/NAK/...NAK's ten times.../"Retry or Quit?"(Q)/CAN/A>...
     15H                                           18H

Sending Computer:

CSUM/---/...Garbled Data....../-----------------------/A>...
8bit

 A final considerations when using the MODEM program,  is  a 
timing related problems when transfer status messages and/or 
textual  data is directed to the screen of a slow (4800 Baud 
or less) terminal or to a hard copy printer. This problem is 
readily  apparent (multiple NAK's) when using MODEM for  the 
first time, and can usually be "cured" by NOT SPECIFYING the 
"V"  (video)  sub-option when sending  or  receiving  files. 
Users  of  Lifeboat  Associates  BSTAM  encounter  the  same 
problem, but this is easily fixed with the files TQPATCH.ASM 
and  RQPATCH.ASM  (transfer quiet/receive quiet) that  Keith 
Petersen (Royal Oak CP/M,  "call-back" remote system, (313)-
588-7054)  wrote to solve the problem of low speed  terminal 
I/O.

 For users of CBBS's that do not have MODEM.ASM (but DO HAVE 
a  CP/M disk system...ESSENTIAL!),  let me suggest that  you 
"data  capture" the file MBOOT3.ASM from one of  the  RCPM's 
(it's  a small 8 kilo-byte file that "fits" in most system's 
memory) to get the larger MODEM.ASM (40  kilo-bytes).  Check 
it  very carefully for errors using the "data capture" (read 
ERROR  PRONE method here).  Then edit and assemble for  your 
modem  configuration.

 If  you are tired of buying software where the advertisment 
is written better than the program, then the RCPM's are just 
what you have been looking for...and FREE!

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


             Software Tricks for the 8080/Z80

                           by

               Kelly Smith, CP/M-Net "SYSOP"

                     January 8, 1980

 In  my  travels  through the  software  written  by  others 
(articles,  disassemblies,  etc.),  I  occasionaly find some 
"tricks"   incorporated  to  either  optimize  the   storage 
requirements of the code (typically ROM based) or to attempt 
to  confuse  disassembly...although they are not recommended 
routines, I think you may find them of interest...

                      The LXI Trick

 Many  large mainframe computers (mini's and maxi's) have  a 
SKIP   instruction...but   most   micro's   are   multi-byte 
instruction  oriented,  and  therefore it  is  difficult  to 
provide    a   SKIP   when   the   instruction   length   is 
indeterminate. First, an example of "straight" coding:

error1:   mvi  a,1  ; set-up error code 1
          jmp  error$handler
error2:   mvi  a,2  ; set-up error code 2
          jmp  error$handler
error3:   mvi  a,3  ; set-up error code 3, and fall into it
;
error$handler:      ; all error codes come here
;
          lxi  d,error$message     ; point to error message,
                                   ; error code in A reg.
          .
          .
          .


 This  is easy enough to understand (right?),  but  consider 
this:

lxib      equ  1    ; equate first byte of LXI b,nnnn
;
error1:   mvi  A,1  ; set-up error code 1
          db   lxib ; first byte of LXI B,nnnn
error2:   mvi  A,2  ; set-up error code 2
          db   lxib ; first byte of LXI B,nnnn
error3:   mvi  A,3  ; set-up error code 3
          lxi  d,error$message     ; point to error message,
                                   ; error code in A reg.  
          .
          .
          .

 If a jump is made to ERROR1, the E Reg. is set-up, then the 
LXIB will be executed, the B&C Regs. will be given "garbage" 
code that follows it,  and finally the program counter  will 
be  incremented  past the next instruction...it  SKIP's  and 
falls   into  the  eventual  output  routine...insidious  to 
disassemblers!  This  was  one of the "favorite's"  at  MITS 
(remember the Altair?).

                     The ORI Trick

You might code:

and$function:       ; indicate boolean AND
;
          mvi  a,1       ; set flags to non-zero, this is AND
          jmp  do$boolean; do boolean function
;
or$function:        ; indicate boolean OR
;
          xra  a         ; set flags to zero, this is OR
;
do$boolean:         ; boolean functions come here
;
          your code...   ; do something...anything!
          .
          .
          .


But consider the following:

ori  equ  0f6h      ; equate first byte of ORI n
;
and$function:       ; indicate boolean AND
;
          db   ori       ; set flags with A reg. not zero,
                    ; first byte of ORI
or$function:        ; indicate boolean OR
;
          xra  a         ; set flags to zero, this is OR
;
do$boolean:         ; boolean functions come here
;
          your code...   ; is everyone confused?
          .
          .
          .

 This   one  is  particulary  clever;   when   entering   at 
AND$FUCTION,  the  ORI picks-up the "XRA A" as F6  Hex,  and 
automatically  set's the flags non-zero. Let me suggest that  
if  you actually use the LXI or ORI trick...comment  it WELL  
in   your   source  code...you may  have   to   patch   your 
program YEARS LATER, and will this look strange !?!


                      Using XTHL

 A  "cute"  (not fast) example for register "swapping"  when 
all  registers  are  used and must be  saved,  is  shown  as 
follows:

exchange$bc$with$hl:     ; exchange B&C Regs. with H&L Regs.
;
          push b    ; put B&C Regs. on the stack
          xthl      ; H&L Regs. = top stack entry = B&C Regs.
          pop  b    ; B&C Regs. = original H&L Regs.

 Very  often you will code a routine to pass a constant to a 
subroutine, such as:

          mvi  c,1
          call dumb$subroutine
          .
          .
          .
          mvi  c,2
          call dumb$subroutine
          .
          .
          .
          mvi  c,3
          call dumb$subroutine
          .
          .
          .
;
dumb$subroutine:    ; use argument passed in C reg.
          .
          .
          .


 By manipulating the return address,  you can save one  byte 
per CALL as follows:

          call trick$subroutine
          db   1       ; put constant in "return" location
          .
          .
          .
          call trick$subroutine
          db   2       ; put constant in "return" location
          .
          .
          .
          call trick$subroutine
          db   3       ; put constant in "return" location
          .
          .    
          .
;
trick$subroutine:   ; trick subroutine to get constant
;
          xthl      ; H&L Regs. = return address
          mov  c,m  ; get constant pointed to by H&L Regs.
          inx  h    ; bump for return address
          xthl      ; restore the return address and H&L Regs.
;
dumb$subroutine:    ; use argument passed in the C Reg.
;
          .
          .
          .


               The "Indirect Jump" via the Stack

 Try  this trick to save a few bytes,  by faking a "indirect 
jump" via the stack...you might code this routine:

          call get$data$word
          jmp  use$data$word
;
get$data$word: ; get word into H&L regs.
;
          lhld my$data$word   ; fetch my data word
          ret
;
use$data$word: ; use data word in H&L Regs.


 But a more "elegant" (though obtuse) method could be  coded 
as this:

          lxi  h,use$data$word     ; make "indirect address"
          push h    ; save it on the stack
;
get$data$word: ; get word into H&L Regs.
;
          lhld my$data$word   ; fetch my data word
          ret        ; pop stack for address and "jump"

 This  can lead to even trickier manipulations on the  stack 
for return address's...everyone (at one time or another) has 
coded  a  routine to "filter" keyboard  characters,  and  it 
usual looks like this:

          cpi  '.'  ; period character?
          jz   filter
          cpi  ','  ; comma character?
          jz   filter
          cpi  ';'  ; semicolon character?
          jz   filter
          cpi  ':'  ; colon character?
          jz   filter
          .
          .
          .


 But  we  need  to save some bytes,  so we get  tricky  with 
coding like this:

          lxi  b,filter  ; make "FILTER" address
          push b    ; put "FILTER" address on the stack
          cpi  '.'  ; period character?
          rz        ; pop stack and go, if match
          cpi  ','  ; comma character?
          rz        ; pop stack and go, if match
          cpi  ';'  ; semicolon character?
          rz        ; pop stack and go, if match
          cpi  ':'  ; colon character?
          rz        ; pop stack and go, if match
          pop  b    ; no match...adjust the stack
          .
          .
          .

 A popular method for "In line" printing of messages in CP/M 
applications program, is as follows:

          call start     ; go to START, after message
          db   'My Junk Program, Version 1$'
;
start:    pop  d    ; get address of message string
          mvi  c,2  ; CP/M print string function
          call 5    ; let CP/M do the work
          .
          .
          .


                    Register "Moving"

 The tricky way to move the D&E Regs.  to B&C Regs might  be 
as follows:

          push d
          pop  b

But the obvious way (and faster) is just:

          mov  d,b
          mov  e,c


 A really tricky programmer could use the PUSH/POP method to 
affect   the   condition   code...this   "blows-away"   even 
experienced  programmers when they encounter it in  someones 
code!...watch this:

          mvi  c,081h    ; the "flags"
          .
          .
          .
          push b         ; use "cunning set-up" to confuse
          .
          .
          .
          pop  psw       ; do it to it!
          .
          .
          .

 This  has the effect of moving the B reg.  into the A Reg., 
and moving the C Reg.  into the PSW (flags),  with the carry 
and sign bits SET (sign is minus), and all other flags reset
to zero's...this trick causes most programmers to mumble for 
hours...

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

              Helpful CP/M Tip-of-the-Month

 Have  you  ever  fired-up Digital  Research's  DDT  or  SID 
program  debugger's,  specifying the program to debug...It's 
loads,  then goes '?' ,  because it can't find the file, and 
it's  really  on the other diskette (let's say B:)  in  your 
system !?!  So you Control-C out (back to CP/M) and PIP  the 
program to the proper diskette...ARGH!

 So use DDT or SID,  to CHANGE the logged in drive number to 
get  to the file you want...Let's assume that we are  logged 
on  to  the A:  drive,  and our target file to debug  is  on 
B:...follow along:

A>DDT BUMBFILE.COM<cr>
DDT VERS 2.0
?
-

 So  here we sit (not the least bit  amused),  contemplating 
our navels...DO THIS!

-IBUMBFILE.COM<cr>  {set-up temporary FCB with filename.typ}
-S5C<cr>            {Substitute starting at address 5C Hex}
005C 00 02<cr>      {set drive number 2 (B: disk)}
005D 42 .<cr>       {quit substituting}
-R<cr>              {Read BUMBFILE.COM}

 As  if by magic,  the debugger will log on to the B:  disk, 
grab the file,  and read it in for your debug  session!  All 
you have to remember, is that at address 5C Hex is the start 
of the temporary file control block, and that:

     01 equals A: disk
     02 equals B: disk
     03 equals C: disk
     .
     .
     .
     So on, and so on for up to 16 disk drives...


++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
